package com.pansoft.openplanet.util;

import com.pansoft.hd.HDWalletUtil;
import com.pansoft.openplanet.account.model.BtcBean;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.Base58;
import org.bitcoinj.core.DumpedPrivateKey;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.Utils;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.HDKeyDerivation;
import org.bitcoinj.wallet.DeterministicSeed;
import org.bitcoinj.wallet.Wallet;
import org.web3j.utils.Numeric;

import java.io.File;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

/**
 * 比特币助记词
 * 用到了比特币的jar包 org.bitcoinj
 */
public class BTCMnemonicUtils extends HDWalletUtil {
    private static final String TAG = "BTCMnemonicUtils";

    /**
     * 通用的比特币基于bip44协议的助记词路径
     */
    private static String BTC_TYPE = "m/0'/0/0";


    /**
     * 使用助记词种子生成钱包
     *
     * @param ds
     * @return
     */
    public static ECKey generateECKey(DeterministicSeed ds) {
        //todo 方法1
        DeterministicKey deterministicKey = HDKeyDerivation.createMasterPrivateKey(ds.getSeedBytes());
        String[] pathArray = BTC_TYPE.split("/");
        for (int i = 1; i < pathArray.length; i++) {
            ChildNumber childNumber;
            if (pathArray[i].endsWith("'")) {
                int number = Integer.parseInt(pathArray[i].substring(0,
                        pathArray[i].length() - 1));
                childNumber = new ChildNumber(number, true);
            } else {
                int number = Integer.parseInt(pathArray[i]);
                childNumber = new ChildNumber(number, false);
            }
            deterministicKey = HDKeyDerivation.deriveChildKey(deterministicKey, childNumber);
        }
        //  ECKey ecKey= ECKey.fromPrivate(deterministicKey.getPrivKeyBytes());


        //todo 方法2(不正确)
//        DeterministicKeyChain deterministicKeyChain = DeterministicKeyChain.builder().seed(ds).build();
//        //这里运用了BIP44里面提到的算法, 44'是固定的, 后面的一个0'代表的是币种BTC
//        List<ChildNumber> keyPath = HDUtils.parsePath("M/44/0/0/0/0");
//
//        DeterministicKey deterministicKey = deterministicKeyChain.getKeyByPath(keyPath, true);

        //todo 方式1获取公钥私钥（16进制）
//        System.out.println(deterministicKey.getPrivateKeyAsHex());
//        System.out.println(deterministicKey.getPublicKeyAsHex());

        //todo 方式2获取公钥私钥
        BigInteger privKeyBTC = deterministicKey.getPrivKey();
        ECKey ecKey = ECKey.fromPrivate(privKeyBTC);

        String publickey = Numeric.toHexStringNoPrefixZeroPadded(new BigInteger(ecKey.getPubKey()), 66);
        //正式环境应该是主网参数
        String privateKey = ecKey.getPrivateKeyEncoded(BtcNetParamUtil.getBtcNetParams()).toString();

        System.out.println(privateKey);
        System.out.println(publickey);
//        System.out.println("ecKey.toAddress:" + ecKey.toAddress(BtcNetParamUtil.getBtcNetParams()));

        Address myAddress = LegacyAddress.fromKey(BtcNetParamUtil.getBtcNetParams(), ecKey);
        System.out.println("ecKey.toAddress:" + myAddress);


        return ecKey;
    }

    /**
     * todo 使用助记词种子生成钱包（经过测试，生成的是一致的）
     *
     * @param ds
     * @return
     */
    public static void generateECKey2(DeterministicSeed ds) {
        org.bitcoinj.wallet.Wallet restoredWallet = org.bitcoinj.wallet.Wallet.fromSeed(BtcNetParamUtil.getBtcNetParams(), ds);

        DeterministicKey deterministicKey = restoredWallet.currentReceiveKey();
        System.out.println("deterministicKey:" + deterministicKey);
        BigInteger privKeyBTC = deterministicKey.getPrivKey();
        ECKey ecKey = ECKey.fromPrivate(privKeyBTC);

//        ECKey firstKey1 = restoredWallet.getActiveKeyChain().getIssuedReceiveKeys().get(0);
//        String publickey1 = Numeric.toHexStringNoPrefixZeroPadded(new BigInteger(firstKey1.getPubKey()), 66);
//        System.out.println("publickey1:" + publickey1);


        String publickey = Numeric.toHexStringNoPrefixZeroPadded(new BigInteger(ecKey.getPubKey()), 66);
        //正式环境应该是主网参数
        String privateKey = ecKey.getPrivateKeyEncoded(BtcNetParamUtil.getBtcNetParams()).toString();
        System.out.println("----------2");
        System.out.println("privateKey:" + privateKey);
        System.out.println("publickey:" + publickey);
        System.out.println("currentReceiveAddress:" + restoredWallet.currentReceiveAddress());


    }

    public static void main(String[] args) {
        // DeterministicSeed ds = createRandomDeterministicSeed("");
        // generateECKey(ds);
        //generateECKey2(ds);
        try {
//           restoreByMnemonic("chief invest small suffer manage sure people account lonely cousin vibrant can", "");
            restoreByPrivateKey("L2yyfrVBoaN9qVTxCM7obLF525fdLx3TSeZToyA4hrxBX2MxFBq8", "");
            // restoreByMnemonic("awkward decorate lunar fancy foster write taxi column family derive tattoo absent", "");

//            restoreByMnemonic("chief invest small suffer manage sure people account lonely cousin vibrant can", "");
//            restoreByPrivateKey("KyCfRAJiTpbhVnALqRy2MafcM8shNnnYddUnKqpRqBL5DficNK6v","");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用助记词和密码生成钱包文件
     *
     * @param mnemonic 助记词
     * @param passWord 密码(密码现在使用"")
     * @return
     * @throws Exception
     */
    public static BtcBean restoreByMnemonic(String mnemonic, String passWord) throws Exception {
        String[] array = mnemonic.split(" ");
        if (array == null || array.length != 12) {
            throw new Exception("助记词错误");
        }
        List<String> list = new ArrayList<>();
        for (int i = 0; i < array.length; i++) {
            list.add(array[i]);
        }
        long creationTimeSeconds = System.currentTimeMillis() / 1000;
        //种子
        DeterministicSeed ds = new DeterministicSeed(list, null, passWord, creationTimeSeconds);
        //生成钱包
        org.bitcoinj.wallet.Wallet restoredWallet = org.bitcoinj.wallet.Wallet.fromSeed(BtcNetParamUtil.getBtcNetParams(), ds);
        //获取deterministicKey
        DeterministicKey deterministicKey = restoredWallet.currentReceiveKey();
        System.out.println("deterministicKey:" + deterministicKey);

        BigInteger privKeyBTC = deterministicKey.getPrivKey();
        //生成eckey
        ECKey ecKey = ECKey.fromPrivate(privKeyBTC);
        //公钥
//        String publickey = Numeric.toHexStringNoPrefixZeroPadded(new BigInteger(ecKey.getPubKey()), 66);
        String publickey = ecKey.getPublicKeyAsHex();
        //私钥
//        String privateKey = ecKey.getPrivateKeyEncoded(BtcNetParamUtil.getBtcNetParams()).toString();
        String privateKey = ecKey.getPrivateKeyAsWiF(BtcNetParamUtil.getBtcNetParams()).toString();
        //地址
        String btcAddress = restoredWallet.currentReceiveAddress().toString();

        System.out.println("privateKey:" + privateKey);
        System.out.println("publickey:" + publickey);
        System.out.println("currentReceiveAddress:" + restoredWallet.currentReceiveAddress());
        //System.out.println("ecKey.toAddress:" + ecKey.toString(BtcNetParamUtil.getBtcNetParams()));

        Address myAddress = LegacyAddress.fromKey(BtcNetParamUtil.getBtcNetParams(), ecKey);

        System.out.println("ecKey.toAddress:" + myAddress);

        BtcBean btcBean = new BtcBean.Builder()
                .btcAddress(btcAddress)
                .btcPrivKey(privateKey)
                .btcPubKey(publickey)
                .wallet(restoredWallet)
                .build();

        return btcBean;


    }

    /**
     * 使用私钥生成钱包文件
     *
     * @param privateKeyString
     * @param passWord         密码(密码现在使用"")
     * @return
     * @throws Exception
     */
    //public static final BaseEncoding HEX = BaseEncoding.base16().lowerCase();
    public static void restoreByPrivateKey(String privateKeyString, String passWord) throws Exception {

        ECKey ecKey;
        if (privateKeyString.length() == 51 || privateKeyString.length() == 52) {
            DumpedPrivateKey dumpedPrivateKey = DumpedPrivateKey.fromBase58(BtcNetParamUtil.getBtcNetParams(), privateKeyString);
            ecKey = dumpedPrivateKey.getKey();
        } else {
            BigInteger privKey = Base58.decodeToBigInteger(privateKeyString);
            ecKey = ECKey.fromPrivate(privKey);
        }


//        DeterministicKey deterministicKey = DeterministicKey.deserializeB58(privateKeyString,BtcNetParamUtil.getBtcNetParams());
//        System.out.println("deterministicKey.getPrivKey："+deterministicKey.getPrivKey());
        System.out.println("ecKey.getPrivKey：" + ecKey.getPrivKey());
        String privateKey = ecKey.getPrivateKeyEncoded(BtcNetParamUtil.getBtcNetParams()).toString();
        //测试表明eckey 没问题
        System.out.println(privateKey);
        // System.out.println(ecKey.getPrivateKeyEncoded(BtcNetParamUtil.getBtcNetParams()));
        System.out.println(ecKey.getPublicKeyAsHex());

        // System.out.println(  ecKey.toStringWithPrivate(BtcNetParamUtil.getBtcNetParams()));
        Address myAddress = LegacyAddress.fromKey(BtcNetParamUtil.getBtcNetParams(), ecKey);

        System.out.println("ecKey.toAddress:" + myAddress);

        if (true) {
            return;
        }

        org.bitcoinj.wallet.Wallet restoredWallet = null;
        restoredWallet.importKey(ecKey);

//        List<ECKey> ecKeyList = new ArrayList<>();
//        ecKeyList.add(ecKey);

        // org.bitcoinj.wallet.Wallet restoredWallet = org.bitcoinj.wallet.Wallet.fromKeys(BtcNetParamUtil.getBtcNetParams(), ecKeyList);
//        org.bitcoinj.wallet.Wallet restoredWallet = org.bitcoinj.wallet.Wallet.fr(BtcNetParamUtil.getBtcNetParams(), ds);
        DeterministicKey deterministicKey = restoredWallet.currentReceiveKey();

        System.out.println("deterministicKey:" + deterministicKey);

        BigInteger privKeyBTC = deterministicKey.getPrivKey();
        ecKey = ECKey.fromPrivate(privKeyBTC);
        String publickey = Numeric.toHexStringNoPrefixZeroPadded(new BigInteger(ecKey.getPubKey()), 66);
        //正式环境应该是主网参数
        //String privateKey = ecKey.getPrivateKeyEncoded(BtcNetParamUtil.getBtcNetParams()).toString();

        System.out.println("privateKey:" + privateKey);
        System.out.println("publickey:" + publickey);
        System.out.println("currentReceiveAddress:" + restoredWallet.currentReceiveAddress());
//        Address destination = Address.fromBase58(BtcNetParamUtil.getBtcNetParams(), privateKeyString);
//        System.out.println("destinationAddress:" + destination);

    }

//    private static EthHdWallet createEthWallet(DeterministicSeed ds, String[] pathArray, String password) {
//        //种子
//        byte[] seedBytes = ds.getSeedBytes();
//        System.out.println(Arrays.toString(seedBytes));
//        //助记词
//        List<String> mnemonic = ds.getMnemonicCode();
//        System.out.println(Arrays.toString(mnemonic.toArray()));
//
//        if (seedBytes == null)
//            return null;
//        DeterministicKey dkKey = HDKeyDerivation.createMasterPrivateKey(seedBytes);
//        for (int i = 1; i < pathArray.length; i++) {
//            ChildNumber childNumber;
//            if (pathArray[i].endsWith("'")) {
//                int number = Integer.parseInt(pathArray[i].substring(0,
//                        pathArray[i].length() - 1));
//                childNumber = new ChildNumber(number, true);
//            } else {
//                int number = Integer.parseInt(pathArray[i]);
//                childNumber = new ChildNumber(number, false);
//            }
//            dkKey = HDKeyDerivation.deriveChildKey(dkKey, childNumber);
//        }
//        System.out.println("path " + dkKey.getPathAsString());
//
//        ECKeyPair keyPair = ECKeyPair.create(dkKey.getPrivKeyBytes());
//        System.out.println("privateKey " + keyPair.getPrivateKey().toString(16));
//        System.out.println("publicKey " + keyPair.getPublicKey().toString(16));
//
//        EthHdWallet ethHDWallet = null;
//        try {
//            WalletFile walletFile = Wallet.createLight(password, keyPair);
//            System.out.println("address " + "0x" + walletFile.getAddress());
//            ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
//            String jsonStr = objectMapper.writeValueAsString(walletFile);
//            System.out.println("keystore " + jsonStr);
//
//            ethHDWallet = new EthHdWallet(keyPair.getPrivateKey().toString(16),
//                    keyPair.getPublicKey().toString(16),
//                    mnemonic, dkKey.getPathAsString(),
//                    "0x" + walletFile.getAddress(), jsonStr);
//        } catch (CipherException | JsonProcessingException e) {
//            e.printStackTrace();
//        }
//
//        return ethHDWallet;
//    }

    /**
     * 根据秘钥文件，导出助记词
     *
     * @param keystorePath
     * @return
     */
    public static String exportBtcMnemonics(String keystorePath) {
        try {
            Wallet wallet = Wallet.loadFromFile(new File(keystorePath));
            System.out.println(wallet.toString());
            DeterministicSeed seed = wallet.getKeyChainSeed();
            System.out.println("creation time: " + seed.getCreationTimeSeconds());
            if (seed.getMnemonicCode() == null) {
                return null;
            }
            String mnemonics = Utils.SPACE_JOINER.join(seed.getMnemonicCode());
            System.out.println("mnemonicCode: " + mnemonics);
            return mnemonics;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static class EthHDWallet {
        String privateKey;
        String publicKey;
        List<String> mnemonic;
        String mnemonicPath;
        String Address;
        String keystore;

        public EthHDWallet(String privateKey, String publicKey, List<String> mnemonic, String mnemonicPath, String address, String keystore) {
            this.privateKey = privateKey;
            this.publicKey = publicKey;
            this.mnemonic = mnemonic;
            this.mnemonicPath = mnemonicPath;
            this.Address = address;
            this.keystore = keystore;
        }
    }

}